Android Kotlin MVP 架构搭建

Github地址:https://github.com/cn-ljb/mvp-kotlin -> Star 一下o(∩_∩)o

MVPKotlin

快捷、高效、低耦合的Android MVP架构,支持Java、Kotlin混编。

扫码体验:
qrcode.png

集成方式

  • 1、Project的build.gradle文件添加如下代码:
allprojects {
    repositories {
          ...
        maven { url 'https://jitpack.io' }
    }
}
  • 2、主Module的build.gradle添加依赖:
//mvp core
implementation 'com.github.cn-ljb:kotlin-mvp-lib:1.0.0'
//net lib
implementation 'com.github.cn-ljb:netlib:1.0.0'
//dao lib
implementation 'com.github.cn-ljb:daolib:1.0.0'

lib源码:kotlin-mvp-libnet-libdao-lib

概述

为什么要使用MVP架构?

通常一般的Android项目结构,我们会在Activity\Fragment中编写大量代码,例如:网络请求、数据填充、页面切换等等,这种项目结构宏观的称之为MVC。

MVC:我们可以把数据源(网络请求、IO…)看作Model层,xml等布局文件看作View层,Activity\Fragment看作Controller层。但在android中xml能力太薄弱了,以至于Activity做了很多本不属于它的工作。

MVP:在MVP架构中Model层与MVC一样作为数据源,不过将Activity\Fragment都看作为View层的一部分负责数据的展示和填充,将Model层与View层的关联操作交给了Presenter层。

该项目架构

mvp.png

特点:

  • 1、V层仅由Activity和Fragmen组成,且仅负责View交互和数据填充工作;
  • 2、M层完全与V层隔离,P层作为V层与M层的桥梁,承担中间人角色(V通过P获取M数据);
  • 3、V层与P层相互持有,通过Constract限制两者的访问域降低耦合;
  • 4、P层通过Factory产出M层Protocol的接口引用降低耦合;
  • 5、Factory产出的M层Protocol是可复用,且内存安全的。

代码示例

  • Contract接口

内部定义IView、IPresenter小接口分别继承IViewContract、IPresenterContract.

interface LoginContract {

    interface IView : IViewContract {
           ...
    }

    interface IPresenter : IPresenterContract {
        fun login()
        ...
    }
}
  • View层

Activity\Fragment继承BaseMvpXxx,在泛型中关联P层约束接口,并实现V层约束接口。

class LoginActivity : BaseMvpActivity<LoginContract.IPresenter>(), LoginContract.IView {

    override fun registerPresenter() = LoginPresenter::class.java

    private fun login() {
        getPresenter().login()
    }
    ...
}    
  • Presenter层

Presenter继承BaseMvpPresenter,在泛型中关联V层约束接口,并实现P层约束接口。

class LoginPresenter : BaseMvpPresenter<LoginContract.IView>(), LoginContract.IPresenter {

    override fun login() {
        ...
    }
}
  • 网络请求

1、使用网络库前,需要先进行初始化,建议放到Application中进行;

2、编写HttpProtocol接口;

3、从HttpFactory中产出HttpProtocol实例。

/**
 * 1、初始化网络库
 * @param1: 接口base url
 * @param2: 公共header
 * @param3: 公共参数
 * @param4: 是否输出日志
 * */
HttpConfig.init(HTTP_API_DOMAIN, headerMap, paramMap, isLog)

/**
 * 2、编写HttpProtocol接口
 * */
interface IUserHttpProtocol {
    /**
     * 通过用户名获取用户信息
     * @param userName 用户名
     * @return  用户基本信息
     * */
    @GET("/users/{userName}")
    fun getUserInfoByName(@Path("userName") userName: String): Observable<User>

    ...
}

/**
 * 3、从HttpFactory中产出HttpProtocol实例 
 * */
HttpFactory.getProtocol(IUserHttpProtocol::class.java)
        .getUserInfoByName(userName)
        .compose(RxUtils.bindToLifecycle(getMvpView()))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe()

是不是很眼熟?该网络库内部是通过 okhttp + retrofit + rxjava 实现。

  • 数据库操作

1、使用数据库前,需要先进行初始化,建议放到Application中进行;

2、继承BaseTable编写Table类;

3、编写DaoProtocol接口;

4、编写DaoProtocol接口实现类;

5、关联DaoProtocol接口与DaoProtocol接口实现类;

6、从DaoFactory中产出DaoProtocol实例。

/**
 * 1、初始化数据库
 * @param1 数据库OpenHelper辅助类(详见项目代码)
 * @param2 DaoProtocol关联类
 * */
DaoConfig.init(dbHelper, protocolConfig)

/**
 * 2、继承BaseTable编写Table类
 * 实现createTableName() 和 createColumns()
 * Time:2019/4/20
 * There is a lot of misery in life
 **/
class UserTable : BaseTable() {

    val COLUMN_ID = BaseColumns._ID
    val COLUMN_USER_ID = "user_id"
    val COLUMN_AVATAR_URL = "avatar_url"
    val COLUMN_NAME = "name"
    ...

    /**
     * 返回表名
     */
    override fun createTableName() = "tb_user"

    /**
     * 返回表字段
     */
    override fun createColumns(): Map<String, String> {
        val tableColumns = HashMap<String, String>()
        tableColumns[COLUMN_ID] = "integer primary key autoincrement"
        tableColumns[COLUMN_USER_ID] = TYPE_TEXT
        tableColumns[COLUMN_AVATAR_URL] = TYPE_TEXT
        tableColumns[COLUMN_NAME] = TYPE_TEXT
        ...
        return tableColumns
    }    
}

/**
 * 3、编写DaoProtocol接口
 **/
interface IUserDaoProtocol : IDaoInterface {

    /**
     * 保存用户
     * */
    fun saveUser(table: UserTable, user: User): Observable<Boolean>
}    

/**
 * 4、编写DaoProtocol接口实现类
 **/
class UserDaoProtocol : BaseDaoProtocol(), IUserDaoProtocol {


    override fun saveUser(table: UserTable, user: User): Observable<Boolean> = createObservable {
        saveUserImpl(table, user)
    }

    private fun saveUserImpl(table: UserTable, user: User): Boolean {
       ...
    }
}

/**
 * 5、关联DaoProtocol接口与DaoProtocol接口实现类
 **/
class ProtocolConfig : IDaoProtocolConfig {

    @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
    override fun <T> transformProtocol(clazz: Class<T>) = when (clazz) {
        IUserDaoProtocol::class.java -> UserDaoProtocol()
        ...
        else -> throw IllegalStateException("not found dao interface object  : ${clazz.name}")
    } as T

}

/**
 * 6、从DaoFactory中产出DaoProtocol实例
 **/
DaoFactory.getProtocol(IUserDaoProtocol::class.java)
        .saveUser(mUserTable, user)
        .compose(RxUtils.bindToLifecycle(getMvpView()))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe()

Kotlin MVP Auto 插件

头晕?Kotlin MVP Auto插件帮你统统搞定。

安装插件

操作: File -> Settings -> Plugins -> Kotlin MVP Auto -> install

plugin_install.png

功能演示

  • 自动生成Contract、View、Presenter Kotlin文件

操作:包目录右键 -> New MVP Kotlin -> 输入模块名称 -> OK

mvp_plugin.gif

  • 自动生成Contract、View、Presenter Java文件

操作:包目录右键 -> New MVP Java -> 输入模块名称 -> OK

后续功能开发中…

截图:

anim.gif